home *** CD-ROM | disk | FTP | other *** search
/ JCSM Shareware Collection 1993 November / JCSM Shareware Collection - 1993-11.iso / cl720 / qbnws33j.lzh / XMODEM.BAS < prev   
BASIC Source File  |  1992-10-23  |  32KB  |  727 lines

  1. '  ╔═══════════════════════════════════════════════════════════════════╗
  2. '  ║                                                                   ║
  3. '  ║   XMODEM.BAS                                Author: Bryan Leggo   ║
  4. '  ║                                                                   ║
  5. '  ║   Original XModem, XModem-CRC, and XModem-1K Transfer Protocols   ║
  6. '  ║                                                                   ║
  7. '  ║   Uses standard QuickLibrary for "FileExists" function. Use /L    ║
  8. '  ║   for QB.QLB in environment or the .LIB while compiling.          ║
  9. '  ║                                                                   ║
  10. '  ╚═══════════════════════════════════════════════════════════════════╝
  11.  
  12. DECLARE FUNCTION CalcCheckSum% (Blk$)
  13. DECLARE FUNCTION CalcCRC& (X$, CRCHigh%, CRCLow%)
  14. DECLARE FUNCTION FileExists% (T$, Attrib%)
  15. DECLARE FUNCTION NoCarrier% ()
  16. DECLARE FUNCTION TimedGet$ (Limit&, Cancelled%)
  17. DECLARE FUNCTION Warn$ (Message$)
  18. DECLARE SUB ClrLn (Ln%, Spaces%)
  19. DECLARE SUB OpenCom (ComChan%, Param$)
  20. DECLARE SUB PurgeBuffer ()
  21. DECLARE SUB ReceiveXModem (BlkSize%, F$)
  22. DECLARE SUB SendXModem (BlkSize%, F$)
  23. DECLARE SUB SimpleTerminal ()
  24. DECLARE SUB Txt (Side$, T$)
  25. DECLARE SUB Transfer (WhichWay$)
  26. DECLARE SUB VidBar (BarOn%, Col%, Length%)
  27.  
  28. TYPE RegTypeX                                   'Register Type for
  29.    ax    AS INTEGER                             ' Interrupt Calls
  30.    bx    AS INTEGER
  31.    cx    AS INTEGER                             'AX = AH AL
  32.    dx    AS INTEGER                             'BX = BH BL, etc.
  33.    bp    AS INTEGER
  34.    si    AS INTEGER
  35.    di    AS INTEGER
  36.    Flags AS INTEGER
  37.    ds    AS INTEGER
  38.    es    AS INTEGER
  39. END TYPE
  40.  
  41. CONST TRUE = -1, FALSE = 0                      'Boolean Constants
  42.  
  43. DEFINT A-Z
  44.                                                     
  45. DIM SHARED CR$, LF$, BS$, Escape$               'Global String Constants
  46. DIM SHARED Lft$, Rght$, Up$, Down$
  47. DIM SHARED PgUp$, PgDown$
  48. DIM SHARED XOn$, XOff$
  49. DIM SHARED Ack$, Nak$, Soh$, Stx$, Eot$, Can$   'Protocol Pseudo-Constants
  50. DIM SHARED ComBase, Baud&
  51. DIM SHARED Txt1st, TxtMax                       'Used by Txt Sub
  52. DIM SHARED Kolor, BGKolor                       'Screen Colors
  53. DIM SHARED ErrCode, ErrCt                       'Error Number & Count
  54.  
  55. '===========================================================================
  56. '                  I N I T I A L I Z E     V A R I A B L E S
  57. '===========================================================================
  58.  
  59. CR$ = CHR$(13): LF$ = CHR$(10): BS$ = CHR$(8): Escape$ = CHR$(27)
  60. Up$ = CHR$(0) + CHR$(72): Down$ = CHR$(0) + CHR$(80)
  61. Lft$ = CHR$(0) + CHR$(75): Rght$ = CHR$(0) + CHR$(77)
  62. PgUp$ = CHR$(0) + CHR$(73): PgDown$ = CHR$(0) + CHR$(81)
  63. XOn$ = CHR$(17): XOff$ = CHR$(19): Ack$ = CHR$(6): Nak$ = CHR$(21)
  64. Soh$ = CHR$(1): Stx$ = CHR$(2): Eot$ = CHR$(4): Can$ = CHR$(24)
  65.  
  66. Baud& = 2400                                       'Set the BaudRate
  67. Param$ = STR$(Baud&) + ",N,8,1,RS,OP,CD0,DS0"      ' and Com Parameters
  68.  
  69.  
  70. '===========================================================================
  71. '                         M A I N     P R O G R A M
  72. '===========================================================================
  73.  
  74. OpenCom 1, Param$                               'Open Port 1 with Parameters$
  75. SimpleTerminal                                  'Terminal Mode
  76. END
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83. '***************************************************************************
  84. '                        E R R O R     H A N D L E R
  85. '***************************************************************************
  86.  
  87. Handler:
  88. ErrCode = ERR                               'Copy Err # to Global Var
  89. ErrCt = ErrCt + 1                           'Try Statement Causing the Error
  90. IF ErrCt MOD 3 = 0 THEN                     ' Twice Before Giving Up and
  91.    RESUME NEXT: ErrCt = 0                   ' Going to the Next Statement
  92. ELSE
  93.    RESUME
  94. END IF
  95.  
  96. FUNCTION CalcCheckSum (Blk$)                'Returns CheckSum on Blk$
  97.  
  98. C& = 0                                      'Use Long Int to Avoid Overflow
  99. FOR Q = 1 TO LEN(Blk$)
  100.    C& = C& + ASC(MID$(Blk$, Q, 1))          'Add to Add Bits of Each Byte
  101. NEXT Q
  102. C& = (C& AND 255)                           'AND Out Hi Byte Bits
  103. CalcCheckSum = C&
  104. END FUNCTION
  105.  
  106. FUNCTION CalcCRC& (B$, CRCHigh%, CRCLow%)      'Calculates CRC for Each Block
  107.  
  108. DIM Power(0 TO 7)                              'For the 8 Powers of 2
  109. DIM CRC AS LONG
  110.  
  111. FOR I = 0 TO 7                                 'Calculate Once Per Block to
  112.    Power(I) = 2 ^ I                            ' Increase Speed Within FOR J
  113. NEXT I                                         ' Loop
  114. CRC = 0                                        'Reset for Each Text Block
  115. FOR I = 1 TO LEN(B$)                           'Calculate for Length of Block
  116.    ByteVal = ASC(MID$(B$, I, 1))
  117.    FOR J = 7 TO 0 STEP -1
  118.       TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND Power(J)) = Power(J))
  119.       CRC = ((CRC AND 32767&) * 2&)
  120.       IF TestBit THEN CRC = CRC XOR &H1021&     ' <-- This for 16 Bit CRC
  121.       '*** IF TestBit THEN CRC = CRC XOR &H8005&     ' <-- This for 32 Bit CRC
  122.    NEXT J
  123. NEXT I
  124. CRCHigh% = (CRC \ 256)                          'Break Word Down into Bytes
  125. CRCLow% = (CRC MOD 256)                         ' for Comparison Later
  126. ComputeCRC& = CRC                               'Return the Word Value
  127. END FUNCTION
  128.  
  129. REM $DYNAMIC
  130. SUB ClrLn (Ln, Spaces)                       'Clears Line from Left Side
  131. LOCATE Ln, 1, 0: PRINT SPACE$(Spaces);       ' for Number of Designated
  132. LOCATE Ln, 1                                 ' Spaces. Returns Cursor to
  133. END SUB                                      ' to First Column Afterwards
  134.  
  135. REM $STATIC
  136. FUNCTION FileExists (T$, Attrib)     'True if File T$ Exists else False
  137.  
  138. DIM F AS STRING * 64
  139. DIM Inx AS RegTypeX
  140. DIM Outx AS RegTypeX
  141.  
  142. Inx.ax = &H2F00                      'Function 2FH Gets the DTA Address in
  143. CALL INTERRUPTX(&H21, Inx, Outx)     ' ES:BX
  144. DTASeg = Outx.es
  145. DTAAddr = Outx.bx
  146. F$ = LTRIM$(RTRIM$(UCASE$(T$))) + CHR$(0)
  147.  
  148. Inx.ds = VARSEG(F$)                  'Pass the File Specs by Giving Address
  149. Inx.dx = VARPTR(F$)                  ' of String that Contains Specification
  150. Inx.ax = &H4E00                      'Function 4EH for Find 1st Matching Entry
  151. Inx.cx = Attrib                      'CX = Directory Attribute (0=Files Only)
  152. CALL INTERRUPTX(&H21, Inx, Outx)     'Use Interrupt 21H
  153. IF Outx.Flags AND 1 THEN
  154.    FileExists = FALSE
  155. ELSE
  156.    FileExists = TRUE
  157. END IF
  158.  
  159. END FUNCTION
  160.  
  161. REM $DYNAMIC
  162. FUNCTION NoCarrier
  163.  
  164. DEF SEG = &H40
  165. IF (INP(ComBase + 6) AND 128) = 0 THEN NoCarrier = TRUE ELSE NoCarrier = FALSE
  166. DEF SEG
  167.  
  168. END FUNCTION
  169.  
  170. REM $STATIC
  171. SUB OpenCom (ComChan, Param$)
  172.  
  173. CLOSE 1
  174. SELECT CASE ComChan                   'Will Require Swapping at &H400, &H402
  175. CASE 1                                ' Order to Support Com 3 and 4
  176.    ComBase = &H3F8
  177.    OPEN "R", 1, "COM1:" + Param$
  178. CASE 2
  179.    ComBase = &H2F8
  180.    OPEN "R", 1, "COM2:" + Param$
  181. END SELECT
  182.  
  183. END SUB
  184.  
  185. SUB PurgeBuffer                                    'Clear Comm Line of Chars
  186.  
  187. Mark& = TIMER                                      'Mark Starting Time
  188. DO
  189.    IF NOT EOF(1) THEN                              'Get More Chars While Some
  190.       JunkIt$ = INPUT$(1, 1): Mark& = TIMER        ' In the Buffer and it's
  191.    END IF                                          ' Less Than 1/2 Second
  192. LOOP UNTIL EOF(1) AND (ABS(TIMER - Mark&) > .5)    ' Since Last Char Gotten
  193. END SUB
  194.  
  195. SUB ReceiveXModem (BlkSize, F$)                 '(Block Size and Filename)
  196. DIM B$(1 TO 4)                                  'Temp Storage of Block Bytes
  197.  
  198. CLOSE 9: OPEN "O", #9, F$                       'Save File to Channel #9
  199. PRINT #1, XOff$; XOn$;
  200. Cancels$ = STRING$(3, Can$)
  201. Underway = FALSE                                'True After 1st Pkt Confirmed
  202. Blocks = 1                                      'Block/Pkt Counter (1-Max)
  203. BlkNum = 1                                      'Packet Block Number (1-255)
  204. Bad = 0                                         'Bad Packets/Error Count
  205. BCt = 0                                         'RAM Block Ptr for B$()
  206. PurgeBuffer                                     'Get Rid of Extra Chars
  207. CrcMode = TRUE: PktSize = BlkSize + 5           'Try CRC Mode First
  208. PRINT #1, "C";                                  'Send "C" to Signal It
  209.  
  210.  
  211. GetPacket:                                      'Get Packet of Bytes
  212. IF NoCarrier THEN ErrType = 13: GOTO ShowErr    'Are We Still Online?
  213. Pkt$ = ""
  214. FOR Tries = 1 TO 10                             'Allow 10 Tries
  215.    W$ = TimedGet$(8, Cancelled)                 'Get Response/1st Char of Pkt
  216.    IF Cancelled THEN ErrType = 11: GOTO ShowErr 'Quit If User Cancelled
  217.    SELECT CASE W$                               '1st Byte Is:
  218.    CASE Soh$: BlkSize = 128: EXIT FOR           'Soh = 128 Byte Block Coming
  219.    CASE Stx$: BlkSize = 1024: EXIT FOR          'Stx = 1K Block Coming
  220.    CASE Eot$: GOTO ReceptionDone                'End of Xmission. Close Out.
  221.    CASE Can$: EXIT FOR                          'Cancelled by Sender
  222.    CASE ""                                      'No Char In Means Timed Out
  223.       Bad = Bad + 1: LOCATE 7, 40
  224.       PRINT "Tries:"; Tries; TAB(80);
  225.    CASE ELSE                                    'Else Didn't Get An Expected
  226.       PurgeBuffer                               ' Response So Purge Characters
  227.    END SELECT
  228.    IF NOT Underway THEN                         'Handshaking Not Complete Yet
  229.       IF Tries < 4 THEN                         ' So Send Out Init Char Again
  230.          CrcMode = TRUE: PRINT #1, "C";         ' Send a "C" to Start CRC or
  231.       ELSE                                      ' a <Nak> for Standard Mode
  232.          CrcMode = FALSE: PRINT #1, Nak$;
  233.       END IF
  234.    END IF
  235.    IF Bad >= 10 THEN                            'Have Reached the Max of 10
  236.       ErrType = 14: PurgeBuffer: GOTO ShowErr   ' Errors from TimeOuts or
  237.    END IF                                       ' Bad Packets so Abort
  238. NEXT Tries
  239. IF CrcMode THEN                                 'Blk Size Determined by <Soh>
  240.    PktSize = BlkSize + 5                        ' or <Stx>, PacketSize by
  241. ELSE                                            ' BlockSize and Type of Check
  242.    PktSize = BlkSize + 4                        ' Used (1 Extra Byte for CRC)
  243. END IF
  244. Pkt$ = W$                                       'We've Got the First Byte
  245. WHILE LEN(Pkt$) <= PktSize - 1                  'Now Get Rest of Packet
  246.    W$ = TimedGet$(4, Cancelled)
  247.    IF Cancelled THEN ErrType = 11: GOTO ShowErr
  248.    IF LEN(W$) THEN                              'If There is a Byte then Add
  249.       Pkt$ = Pkt$ + W$                          ' it to the Packet
  250.       IF LEFT$(Pkt$, 3) = Cancels$ THEN         'Packet Starting with Three
  251.          PRINT #1, Cancels$; Ack$;              ' <Can>s Is a Cancellation So
  252.          ErrType = 12: GOTO ShowErr             ' <Ack>nowledge And Abort
  253.       END IF
  254.    ELSE                                         'Else Null Means We Timed Out
  255.       Bad = Bad + 1
  256.       LOCATE 7, 40: PRINT TAB(80);
  257.       LOCATE 7, 40: PRINT "Character Timeout. Errors:"; Bad;
  258.       GOTO CheckPacket
  259.    END IF
  260. WEND
  261.  
  262. CheckPacket:                                              'Check Packet Errors
  263. IF LEN(Pkt$) = PktSize THEN                               'If Packet Right Size
  264.    IF BlkNum = ASC(MID$(Pkt$, 2, 1)) + 1 AND (BlkNum XOR 255) = ASC(MID$(Pkt$, 3, 1)) THEN
  265.       ErrType = 7: GOTO ShowErr                           'Repeated Block #
  266.    ELSEIF BlkNum <> ASC(MID$(Pkt$, 2, 1)) THEN            'Block Counts Don't
  267.       ErrType = 5: GOTO ShowErr                           ' Match. Try New Pkt
  268.    ELSEIF (BlkNum XOR 255) <> ASC(MID$(Pkt$, 3, 1)) THEN  'Block Ct Complement
  269.       ErrType = 6: GOTO ShowErr                           ' Mismatch. Try New
  270.    END IF                                                 ' Packet
  271.    Blk$ = MID$(Pkt$, 4, BlkSize)                          'Else Copy the Block
  272.    IF CrcMode THEN                                        'Do CheckSum or CRC
  273.       J& = CalcCRC&(Blk$, Hi, Low)
  274.       IF Hi <> ASC(MID$(Pkt$, PktSize - 1, 1)) THEN ErrType = 4: GOTO ShowErr
  275.       IF Low <> ASC(MID$(Pkt$, PktSize, 1)) THEN ErrType = 4: GOTO ShowErr
  276.    ELSE
  277.       ChkSum = CalcCheckSum(Blk$)
  278.       IF ChkSum <> ASC(MID$(Pkt$, PktSize, 1)) THEN ErrType = 3: GOTO ShowErr
  279.    END IF
  280.    GOSUB ShowProgress                             'Displays Xfer Status
  281.    BlkNum = 255 AND (BlkNum + 1)                  'Success Thru All CheckPts
  282.    Blocks = Blocks + 1: Bad = 0                   ' so Increment Block Cts
  283.    Underway = TRUE                                ' Mark Handshake Completed
  284.    IF BlkSize = 1024 THEN                         'For Xmodem-1k Write to Disk
  285.       PRINT #9, Blk$;                             ' Immediately
  286.    ELSE
  287.       BCt = BCt + 1: B$(BCt) = Blk$               'Else Save 4 Blocks In RAM
  288.       IF BCt = 4 THEN                             ' Write them to Disk Every
  289.          PRINT #9, B$(1); B$(2); B$(3); B$(4);    ' 4th, i.e. After 512 Bytes
  290.          BCt = 0                                  ' Reset RAM Block Index
  291.       END IF
  292.    END IF                                         'Acknowledge Good Block Read
  293.    PRINT #1, Ack$;                                ' And Go to Get Next Packet
  294.    GOTO GetPacket
  295. ELSEIF LEN(Pkt$) < PktSize THEN                   'Packet Too Short so Show
  296.    ErrType = 1: GOTO ShowErr                      ' Err and Get New Packet
  297. ELSEIF LEN(Pkt$) > PktSize THEN                   'Packet Too Big so Show Err
  298.    ErrType = 2: GOTO ShowErr                      ' And Get New Packet
  299. ELSE                                              'Else an Unexpected Error
  300.    ErrType = 8: GOTO ShowErr                      ' So Warn and Try for New
  301. END IF                                            ' Packet
  302.                                                   ' Last 2 Should NOT Occur
  303.  
  304. ReceptionDone:
  305. IF BCt <> 0 THEN                                  'If Some Bytes Still In
  306.    FOR I = 1 TO BCt: PRINT #9, B$(I); : NEXT I    ' Memory Then Write Them
  307. END IF                                            ' to Disk
  308. CLOSE 9: PRINT #1, Ack$;                          'Xmit Complete so Close
  309. EXIT SUB                                          ' File and Send Final Ack
  310.  
  311.  
  312. '---------------------------------------------------------------------------
  313.  
  314. ShowErr:
  315. Response$ = Nak$                                  'Send Nak After Most Errors
  316. SELECT CASE ErrType
  317. CASE 1: ErM$ = "Short Block in #" + STR$(Blocks)
  318. CASE 2: ErM$ = "Long Block in #" + STR$(Blocks)
  319. CASE 3: ErM$ = "Checksum Error in #" + STR$(Blocks)
  320. CASE 4: ErM$ = "CRC Error in #" + STR$(Blocks)
  321. CASE 5: ErM$ = "Block # Error in #" + STR$(Blocks)
  322. CASE 6: ErM$ = "Complement Error in #" + STR$(Blocks)
  323. CASE 7: ErM$ = "Block # Repeated in #" + STR$(Blocks - 1): Response$ = Ack$
  324. CASE 8: ErM$ = "Unexpected Error!"
  325. CASE 9:
  326. CASE 10: ErM$ = "Transfer Cancelled"
  327. CASE 11: ErM$ = "Transfer Aborted by User"
  328. CASE 12: ErM$ = "Transfer Aborted by Sender"
  329. CASE 13: ErM$ = "No Carrier"
  330. CASE 14: ErM$ = "Maximum Errors. Transfer Aborted."
  331. END SELECT
  332. LOCATE 7, 40: PRINT TAB(80);                      'Show the ErrorMsg
  333. LOCATE 7, 40: PRINT ErM$;
  334. IF ErrType < 10 THEN                              'ErrType < 10 is Recoverable
  335.    Bad = Bad + 1                                  ' Count One More Error
  336.    PRINT #1, Response$;                           ' Respond Nak (or Ack) and
  337.    Pkt$ = "": GOTO GetPacket                      ' Go to Get Packet Again
  338. ELSE
  339.    J$ = Warn$(ErM$)                               'Notify User of Cancel
  340.    SLEEP 2: PurgeBuffer                           'Get Rid of Remaining Pkt
  341.    PRINT #1, STRING$(5, 24); STRING$(5, 8);       'Send 5 <Can>s & 5 <BS>s
  342.    CLOSE 9: KILL F$                               'ErrType >= 10 is Fatal so
  343.    EXIT SUB                                       ' Kill Off File and Quit
  344. END IF
  345.  
  346. '---------------------------------------------------------------------------
  347.  
  348. ShowProgress:                                     'Show Byte Counts & Bar
  349. KBytes = INT(Blocks * (BlkSize / 1024))
  350. LOCATE 5, 40: PRINT "Received #"; Blocks; TAB(60); KBytes; "K Bytes";
  351. IF BarLength = 0 THEN
  352.    LOCATE 9: VidBar FALSE, 1, 80
  353.    FOR K = 1 TO 9
  354.       LOCATE 10, K * 8 - 1
  355.       PRINT LTRIM$(STR$(100 * (KBytes \ 100) + (K * 10))); "K ";
  356.    NEXT K
  357. END IF
  358. BarLength = INT(80 * ((KBytes MOD 100) / 100))
  359. LOCATE 9: VidBar TRUE, 1, BarLength
  360. RETURN
  361.  
  362.  
  363.  
  364. ' Block refers to Block of Text from File (128 bytes, 1024 for Xmodem-1K)
  365. ' Packet Refers to Block + Extra "Control" Characters, i.e. :
  366.  
  367. '       XModem: SOH + BlockCt + Complement BlockCt + Block + CheckSum
  368. '    XModemCRC: SOH + BlockCt + Complement BlockCt + Block + CRC (Hi & Low)
  369. '    XModem-1K: STX + BlockCt + Complement BlockCt + Block + CheckSum
  370. ' XModemCRC-1K: STX + BlockCt + Complement BlockCt + Block + CRC (Hi & Low)
  371.  
  372. END SUB
  373.  
  374. SUB SendXModem (BlkSize, F$)                              '(Bytes, FileName$)
  375.  
  376. CLOSE 9: OPEN F$ FOR RANDOM AS 9 LEN = 128
  377. FIELD #9, 128 AS BlkOf128$
  378. FiLen& = LOF(9): TtlBlocks = FiLen& \ BlkSize             'Get File Length
  379. IF FiLen& MOD BlkSize > 0 THEN TtlBlocks = TtlBlocks + 1  ' in Bytes & Blocks
  380. LOCATE 3, 40: PRINT "Blocks:"; TtlBlocks; TAB(60);
  381. Seconds = ((TtlBlocks * 6) + FiLen&) \ (Baud& \ 16)
  382. Est$ = STR$(Seconds \ 3600) + STR$(Seconds \ 60) + STR$(Seconds MOD 60)
  383. FOR I = 2 TO LEN(Est$)
  384.    IF MID$(Est$, I, 1) = " " THEN MID$(Est$, I, 1) = ":"
  385. NEXT I
  386. PRINT "Est. Time:"; Est$;
  387.  
  388. ErM$ = "Transfer Aborted"                        'Generic Msg In Case of Error
  389. Blocks = 0: BlkNum = 0                           'Blocks (1-?), BlkNum (1-255)
  390. EoFile = FALSE: W$ = ""                          'Initialize Block, Byte,
  391. Ct& = 0                                          'To Count Bytes Used & Sent
  392. Bad = 0                                          'Error Counter
  393. PurgeBuffer                                      'Clear the Com Line
  394.  
  395. DO                                               'Shake Hands with Receiver
  396.    W$ = TimedGet$(20, Cancelled)                 'Get Initial Character
  397.    IF Cancelled THEN GOTO AbortSend              'If User Pressed <Esc>
  398.    SELECT CASE W$
  399.    CASE Can$: GOTO AbortSend                     'Receiver is Cancelling
  400.    CASE Nak$: CrcMode = FALSE: EXIT DO           'Nak for Standard XModem
  401.    CASE "C": CrcMode = TRUE: EXIT DO             'C Indicates XModem-CRC
  402.    END SELECT                                    'Begin After <Nak> or C
  403. LOOP
  404.  
  405. MakePacket:
  406. IF NoCarrier THEN                                    'Still Online?
  407.    ErM$ = "No Carrier!": GOTO AbortSend
  408. END IF
  409. W$ = "": Blocks = Blocks + 1: Bad = 0                'Advance Block Counter
  410. IF (BlkSize = 1024) AND ((Ct& + 896) > FiLen&) THEN  'If Doing 1k and at End
  411.    BlkSize = 128                                     ' of File Then Shorten
  412. END IF                                               ' to Avoid Extra Nulls
  413. IF BlkSize = 128 THEN MaxBCt = 1 ELSE MaxBCt = 8     '8 Groups of 128 = 1024
  414. BCt = 0: Blk$ = ""                                   'Build the Block$
  415. DO
  416.    Ct& = Ct& + 128: GET #9                           'Advance File Ptr, Get From File
  417.    BCt = BCt + 1: Blk$ = Blk$ + BlkOf128$
  418.    IF Ct& >= FiLen& THEN                             'If It's Last Block We're
  419.       EoFile = TRUE                                  ' About Done Xmitting
  420.       Pad = Ct& - FiLen&                             ' Pad the End with Nulls
  421.       MID$(Blk$, BlkSize - Pad, Pad) = STRING$(Pad, CHR$(0))
  422.       EXIT DO
  423.    END IF
  424. LOOP UNTIL BCt = MaxBCt                              'Done After 1 (8 for 1k)
  425. BlkNum = (255 AND Blocks)                            ' So Assemble the Packet
  426. Pkt$ = Soh$ + CHR$(BlkNum) + CHR$(BlkNum XOR 255) + Blk$
  427. IF BlkSize = 1024 THEN MID$(Pkt$, 1, 1) = Stx$       '1st Byte is Stx for 1K
  428. IF CrcMode THEN                                      'End of Packet Varies
  429.    J& = CalcCRC&(Blk$, Hi%, Low%)                    ' with Check Method Used
  430.    Pkt$ = Pkt$ + CHR$(Hi%) + CHR$(Low%)              ' 2 Bytes for CRC
  431. ELSE
  432.    ChkSum = CalcCheckSum(Blk$)                       ' 1 Byte for CheckSum
  433.    Pkt$ = Pkt$ + CHR$(ChkSum)
  434. END IF
  435.  
  436. SendPacket:
  437. PRINT #1, Pkt$;                                      'Send the Packet and
  438. LOCATE 5, 40: PRINT "Sending #"; Blocks;             ' Show Progress on Screen
  439. P = INT((Blocks / TtlBlocks) * 100)                  'Calculate Percentage
  440. IF P <= 100 THEN                                     'Percentage Can Be > 100
  441.    LOCATE 5, 60: PRINT P; "% Complete": LOCATE 9     ' On Last Blocks of 1k
  442.    VidBar TRUE, 1, INT((Blocks / TtlBlocks) * 80)    ' Mode Since Last 1024 is
  443. END IF                                               ' Sent in 128 Byte Blocks
  444.  
  445. DO                                                   'Packet Has Been Sent so
  446.    W$ = TimedGet$(10, Cancelled)                     'Get Response/Confirm
  447.    IF Cancelled THEN GOTO AbortSend                  'Quit If User <Esc>aped
  448.    SELECT CASE W$                                    'Interpret Response
  449.    CASE Ack$                                         'Block Acknowledged So
  450.       Bad = 0                                        ' Send Next Packet If
  451.       IF EoFile THEN EXIT DO ELSE GOTO MakePacket    ' More Data
  452.    CASE ELSE                                         'Else
  453.       Bad = Bad + 1                                  ' Count 1 More Error
  454.       IF Bad > 9 THEN GOTO AbortSend                 ' Abort If Over Limit
  455.       IF W$ = Can$ THEN                              'If a <Can> Then Look
  456.          FOR I = 1 TO 2                              ' For at Least 2 More to
  457.             W$ = W$ + TimedGet$(2, Cancelled)        ' Be Sure (Or User Esc)
  458.             IF Cancelled THEN GOTO AbortSend
  459.             IF W$ = STRING$(3, Can$) THEN GOTO AbortSend
  460.          NEXT I
  461.          GOTO SendPacket
  462.       ELSE
  463.          PurgeBuffer                                 'Any Other Char Is an
  464.          GOTO SendPacket                             ' Error So ReSend Packet
  465.       END IF                                         ' & Look for <Ack> Again
  466.    END SELECT
  467. LOOP
  468.  
  469. ConcludeSend:
  470. ErM$ = "End of Transmission": GOSUB ShowStatus       'Proper End of Transmit
  471. CLOSE 9: PRINT #1, Eot$;                             'Close File, Send the EOT
  472. I$ = TimedGet$(10, Cancelled)                        'Get Final Char
  473. IF I$ = Ack$ THEN                                    'Should Be an <Ack> but
  474.    ErM$ = "Acknowledged": GOSUB ShowStatus
  475. ELSEIF Cancelled THEN                                'Allow User to Cancel
  476.    EXIT SUB
  477. ELSE                                                 'If Not an <Ack> Resend
  478.    GOTO ConcludeSend                                 ' <Eot> and Try Again
  479. END IF
  480. EXIT SUB
  481.  
  482. '---------------------------------------------------------------------------
  483.  
  484. AbortSend:
  485. J$ = Warn$(ErM$)                                 'Show Error Status
  486. CLOSE 9                                          'Close File
  487. PRINT #1, STRING$(5, Can$); STRING$(5, BS$);     'Send Cancel to Receiver
  488. EXIT SUB
  489.  
  490. '---------------------------------------------------------------------------
  491.  
  492. ShowStatus:
  493. LOCATE 7, 40: PRINT ErM$; TAB(80);               'Show the Status or ErrorMsg
  494. RETURN
  495.  
  496. END SUB
  497.  
  498. SUB SimpleTerminal
  499. ON ERROR GOTO Handler
  500. FF$ = CHR$(12): Hm$ = CHR$(11)
  501.  
  502. CLS : GOSUB InfoBar
  503. PRINT #1, "AT S0=1"                       'Send Modem Initialization String
  504. DO
  505.    Out$ = INKEY$                          'Look for Key Press
  506.    IF LEN(Out$) THEN                      'If There IS One then Select
  507.       SELECT CASE Out$
  508.       CASE PgUp$, PgDown$                 ' to Upload or Download
  509.          Transfer Out$: GOSUB InfoBar
  510.       CASE Escape$                        ' Escape to End Program
  511.          EXIT DO
  512.       CASE CHR$(0) + CHR$(59)
  513.          PRINT #1, "atdt 626-9456"
  514.       CASE ELSE
  515.          PRINT #1, Out$;                  ' Else Send the Character Verbatim
  516.       END SELECT
  517.    END IF
  518.    IF LOC(1) THEN                         'Is there Incoming Data from Com?
  519.       DO                                  ' If So then Get Chars Until No
  520.          ComChr$ = INPUT$(1, 1)           ' More or End of a Line <LF>
  521.          SELECT CASE ComChr$
  522.          CASE BS$: ComChr$ = CHR$(29)     'Replace BackSpaces with CHR$(29)
  523.          CASE FF$, Hm$: ComChr$ = ""      'Filter these Out
  524.          CASE LF$: ComChr$ = "": EXIT DO  'Ignore Linefeeds But Exit Do Loop
  525.          END SELECT
  526.          PRINT ComChr$;                   'Print the Char Received On Screen
  527.       LOOP UNTIL LOC(1) = 0               'No More Com Waiting
  528.    END IF
  529. LOOP
  530. EXIT SUB
  531.  
  532. '---------------------------------------------------------------------------
  533.  
  534. InfoBar:
  535. LOCATE 25, 1: COLOR 0, 7
  536. PRINT " <PgUp> to Upload,  <PgDown> to Download,  <Escape> to End Program"; TAB(80); " ";
  537. COLOR 7, 0: LOCATE 24, 1
  538. RETURN
  539.  
  540. END SUB
  541.  
  542. FUNCTION TimedGet$ (Limit&, Cancelled)            'Timed Routine to Get One
  543.                                                   'Character from Comm Port
  544. Mark& = TIMER                                     'Mark Starting Time
  545. DO
  546.    IF NOT EOF(1) THEN                             'If Chars Waiting Then
  547.       TimedGet$ = INPUT$(1, 1): EXIT FUNCTION     ' Return 1 Character
  548.    END IF
  549.    IF INKEY$ = Escape$ THEN                       'User Can Press <Esc> to
  550.       Cancelled = TRUE: EXIT FUNCTION             ' Quit
  551.    END IF
  552. LOOP WHILE ABS(TIMER - Mark&) < Limit&            'Wait Up Until Past Limit
  553. TimedGet$ = ""                                    'Return "" If Timing Out
  554. END FUNCTION
  555.  
  556. REM $DYNAMIC
  557. SUB Transfer (WhichWay$)                  'WhichWay = PgUp (U/L), PgDn (D/L)
  558. ON ERROR GOTO Handler
  559.  
  560. NumProtos = 4                             'Number of Protocols Here
  561. SendDir$ = ""                             'Define Directories Where Files Will
  562. RecvDir$ = ""                             ' Be DownLoaded To or Uploaded From
  563. SendExternal$ = ""                        'DOS Command Line Used to Execute
  564. RecvExternal$ = ""                        ' External Protocol (~ for Filename)
  565.  
  566. Kolor = 0: BGKolor = 7                    'Transfer Area in Reverse Video for
  567. COLOR Kolor, BGKolor                      ' Contrast
  568. VIEW PRINT 1 TO 11: CLS 2: VIEW PRINT     'Clear Top 11 Lines
  569. LOCATE 11, 1: PRINT STRING$(80, "▒");
  570.  
  571. IF WhichWay$ = PgUp$ THEN                 'Determine if Sending or Receiving
  572.    Way$ = "Sending": Sending = TRUE       ' From Key Pressed
  573. ELSE
  574.    Way$ = "Receiving": Sending = FALSE
  575. END IF
  576. DO
  577.    ClrLn 9, 80: PRINT "File You Are "; Way$; ": ";
  578.    F$ = "": LINE INPUT F$
  579.    IF F$ = "" THEN GOTO ExitTransfer
  580.    F$ = UCASE$(F$)
  581.    IF Sending THEN
  582.       IF LEN(SendDir$) THEN
  583.          IF INSTR(F$, ":") = 0 THEN F$ = SendDir$ + "\" + F$
  584.       END IF
  585.       IF FileExists(F$, 0) THEN Ok = TRUE ELSE J$ = Warn$("File Not Found")
  586.    ELSE
  587.       IF LEN(ReceiveDir$) THEN
  588.          IF INSTR(F$, ":") = 0 THEN F$ = ReceiveDir$ + "\" + F$
  589.       END IF
  590.       IF FileExists(F$, 0) THEN
  591.          ClrLn 9, 80
  592.          PRINT F$; " Already Exists! Overwrite it? (Y/N)? ";
  593.          DO: B$ = UCASE$(INKEY$)
  594.          LOOP UNTIL LEN(B$) AND INSTR("YN", B$)
  595.          IF B$ = "Y" THEN Ok = TRUE
  596.       ELSE
  597.          ErrCode = 0: F = FREEFILE
  598.          OPEN "O", F, F$
  599.          IF ErrCode THEN J$ = Warn$("Bad Path/Filename?") ELSE Ok = TRUE
  600.          CLOSE F
  601.       END IF
  602.    END IF
  603. LOOP UNTIL Ok
  604.  
  605. Txt1st = 1: TxtMax = 30                            'And Draw a Box Around
  606. LOCATE 1, 1
  607. PRINT TAB(40); "Choose a Protocol"; TAB(80);
  608. Txt "T", ""
  609. Txt "C", "       XModem       "
  610. Txt "C", " XModem-1k (YModem) "
  611. Txt "C", " External Protocol  "
  612. Txt "C", "       Cancel       "
  613. Txt "B", ""
  614. R = 1: C = 0
  615. DO
  616.    LOCATE R + 1, 2, 0
  617.    VidBar TRUE, 2, 30
  618.    DO: C$ = INKEY$: LOOP UNTIL LEN(C$)
  619.    VidBar FALSE, 2, 30
  620.    SELECT CASE C$                                      'Based on Terminator:
  621.    CASE Up$: R = R - 1: IF R < 1 THEN R = NumProtos    ' Go to Line Above
  622.    CASE Down$: R = R + 1: IF R > NumProtos THEN R = 1  ' or Line Below
  623.    CASE CR$: EXIT DO
  624.    CASE Escape$: EXIT DO
  625.    END SELECT
  626. LOOP
  627. IF C$ = Escape$ THEN GOTO ExitTransfer                 'Cancelled by User
  628. VidBar TRUE, 2, 30
  629. LOCATE 9, 1: PRINT "╟──+───┼───+───┼───+───┼───+───┼───+───║───+───┼───+───┼───+───┼───+───┼───+───╢"
  630. LOCATE 1, 3: PRINT " Press <Escape> to Cancel "
  631. LOCATE 1, 40: PRINT Way$; ": "; UCASE$(F$); TAB(80);
  632. IF Sending THEN
  633.    LOCATE 10, 1: PRINT "      10%     20%     30%     40%     50%     60%     70%     80%     90%"
  634.    SELECT CASE R
  635.    CASE 1: SendXModem 128, F$
  636.    CASE 2: SendXModem 1024, F$
  637.    CASE 3: Ext$ = SendExternal$: GOSUB InsertFileName: SHELL Ext$
  638.    CASE 4: GOTO ExitTransfer
  639.    END SELECT
  640. ELSE
  641.    SELECT CASE R
  642.    CASE 1: ReceiveXModem 128, F$
  643.    CASE 2: ReceiveXModem 1024, F$
  644.    CASE 3: Ext$ = RecvExternal$: GOSUB InsertFileName: SHELL Ext$
  645.    CASE 4: GOTO ExitTransfer
  646.    END SELECT
  647. END IF
  648. PLAY "T90 O3 L32 CBCBCBCBCBCBCBCBCBCBCBCBCBCBCBC"     'All Done Warning Sound
  649.  
  650. ExitTransfer:
  651. COLOR 7, 0                                            'Back to White on Black
  652. VIEW PRINT 1 TO 11: CLS 2: VIEW PRINT                 'Clear Top 11 Lines
  653. VIEW PRINT 1 TO 24: LOCATE 24, 1, 1
  654. EXIT SUB
  655.  
  656. '----------------------------------------------------------------------------
  657.  
  658. InsertFileName:                    'Substitute FileName for ~ in Strings Used
  659. P = INSTR(Ext$, "~")               ' to Call External Protocol (Send or Recv)
  660. IF P > 1 THEN
  661.    Ext$ = LEFT$(Ext$, P - 1) + F$ + RIGHT$(Ext$, LEN(Ext$) - P)
  662. END IF
  663. RETURN
  664.  
  665. END SUB
  666.  
  667. REM $STATIC
  668. SUB Txt (Side$, Text$)                  'Put 1 Line of Text w/ Box Delimiters
  669.  
  670. IF LEN(Text$) > TxtMax THEN Text$ = LEFT$(Text$, TxtMax - 2)
  671. SpaceLeft = (TxtMax - LEN(Text$)) \ 2
  672. LOCATE , Txt1st
  673. IF LEN(Text$) MOD 2 = 1 THEN Text$ = Text$ + " "
  674. IF Side$ = LCASE$(Side$) THEN Shadow$ = ""
  675. SELECT CASE UCASE$(Side$)
  676. CASE "T"
  677.    Text$ = "╔" + STRING$(TxtMax, "═") + "╗"                    'Top Border
  678.    C = (TxtMax \ 2) - (LEN(T$) \ 2)
  679.    MID$(Text$, C) = T$
  680. CASE "B"
  681.    Text$ = "╚" + STRING$(TxtMax, "═") + "╝"                    'Bottom Border
  682.    C = (TxtMax \ 2) - (LEN(T$) \ 2)
  683.    MID$(Text$, C) = T$
  684. CASE "C"
  685.    Text$ = "║" + STRING$(SpaceLeft, " ") + Text$ + STRING$(SpaceLeft, " ") + "║"
  686. CASE "R"
  687.    Text$ = "║" + STRING$(2 * SpaceLeft, " ") + Text$ + "║"     'Right-Justify
  688. CASE "L"
  689.    Text$ = "║" + Text$ + STRING$(2 * SpaceLeft, " ") + "║"     'Left-Justify
  690. END SELECT
  691.  
  692. PRINT Text$; Shadow$;                                   'Print Text, DeLimits
  693. IF CSRLIN < 24 THEN PRINT                               'Go to Next Line
  694. IF (Side$ = "B") AND LEN(Shadow$) THEN
  695.    IF CSRLIN = 24 THEN LOCATE 25
  696.    LOCATE , Txt1st
  697.    PRINT " "; STRING$(TxtMax + 1, Shadow$); Shadow$;
  698.    Shadow$ = ""
  699. END IF
  700. END SUB
  701.  
  702. SUB VidBar (BarOn, Col, Length)
  703.  
  704. 113 LOCATE , Col                        'Position at Paramter Column
  705. IF BarOn THEN                           'IF Hilighting (BarOn = True) then
  706.    COLOR BGKolor, Kolor                 ' Use the BGKolor in the FG
  707.    FOR J = Col TO Col + Length - 1      'Across the Screen for the "Length"
  708.       PRINT CHR$(SCREEN(CSRLIN, J));    ' Re-Print the Char That is Already
  709.    NEXT J                               ' There in It's New Colors
  710. ELSE
  711.    COLOR Kolor, BGKolor                 'ELSE De-HiLiting So Return Colors
  712.    FOR J = Col TO Col + Length - 1      ' to Normal and Re-Print each Char
  713.       PRINT CHR$(SCREEN(CSRLIN, J));    ' in the Row with the Regular Video
  714.    NEXT J
  715. END IF
  716. LOCATE , Col                            'Return to 1st Column
  717. COLOR Kolor, BGKolor                    ' and Normal Colors
  718. END SUB
  719.  
  720. FUNCTION Warn$ (Warning$)
  721. LOCATE 1, 40: COLOR 20
  722. PRINT " "; Warning$; TAB(80);
  723. COLOR Kolor, BGKolor
  724. BEEP: BEEP
  725. END FUNCTION
  726.  
  727.